﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JOATServer.SocietyFramework.Core.XML
{
    public abstract partial class BaseXMLAutomaton : BaseAutomaton
    {
        /* ******************** LOOP INSTRUCTION CLASSES ******************** */

        /// <summary>
        /// Class which represents a while loop. It contains a test instruction which must return a Boolean value and a repeat
        /// instruction which is executed until the test instruction returns false.<br />
        /// The repeat instruction can be a single instruction or an instruction block. In case of an instruction block if the
        /// block returns LoopSpecialSymbols.CONTINUE or LoopSpecialSymbols.BREAK the specified action is taken. If it returns
        /// null, the while continues with the test instruction. If it returns anything else then it is considered a return value.
        /// </summary>
        protected class WhileInstruction : Instruction, CodeContainer
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction testInstruction, repeatInstruction;

            public WhileInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public object Code()
            {
                object instructionResult = null;
                while ((Boolean)testInstruction.Code())
                {
                    if (repeatInstruction != null)
                    {
                        instructionResult = repeatInstruction.Code();
                    }
                    if (instructionResult != null)
                    {
                        if (instructionResult.Equals(SpecialSymbols.BREAK))
                        {
                            break;
                        }
                        else if (!instructionResult.Equals(SpecialSymbols.CONTINUE))
                        {
                            return instructionResult;
                        }
                    }
                }
                return null;
            }

            public void AddInstruction(Instruction instruction)
            {
                if (this.repeatInstruction != null)
                {
                    if (this.repeatInstruction is InstructionBlock)
                    {
                        ((InstructionBlock)this.repeatInstruction).AddInstruction(instruction);
                    }
                    else
                    {
                        InstructionBlock instructionBlock = new InstructionBlock(Owner);
                        instructionBlock.AddInstruction(this.repeatInstruction);
                        instructionBlock.AddInstruction(instruction);
                        this.repeatInstruction = instructionBlock;
                    }
                }
                else
                {
                    this.repeatInstruction = instruction;
                }
            }

            public Instruction RemoveInstruction()
            {
                if (this.repeatInstruction != null)
                {
                    if (this.repeatInstruction is InstructionBlock)
                    {
                        InstructionBlock instructionBlock = (InstructionBlock)repeatInstruction;
                        if (instructionBlock.Instructions.Count > 2)
                        {
                            return instructionBlock.RemoveInstruction();
                        }
                        else
                        {
                            Instruction lastInstruction = instructionBlock.RemoveInstruction();
                            this.repeatInstruction = instructionBlock.RemoveInstruction();
                            return lastInstruction;
                        }
                    }
                    else
                    {
                        Instruction lastInstruction = this.repeatInstruction;
                        this.repeatInstruction = null;
                        return lastInstruction;
                    }
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Class which represents a do-while loop. It contains a test instruction which must return a Boolean value and a repeat
        /// instruction which is executed first and then until the test instruction returns false.<br />
        /// The repeat instruction can be a single instruction or an instruction block. In case of an instruction block if the
        /// block returns LoopSpecialSymbols.CONTINUE or LoopSpecialSymbols.BREAK the specified action is taken. If it returns
        /// null, the while continues with the test instruction. If it returns anything else then it is considered a return value.
        /// </summary>
        protected class DoWhileInstruction : Instruction, CodeContainer
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction testInstruction, repeatInstruction;

            public DoWhileInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public object Code()
            {
                object instructionResult = null;
                do
                {
                    if (repeatInstruction != null)
                    {
                        instructionResult = repeatInstruction.Code();
                    }
                    if (instructionResult != null)
                    {
                        if (instructionResult.Equals(SpecialSymbols.BREAK))
                        {
                            break;
                        }
                        else if (!instructionResult.Equals(SpecialSymbols.CONTINUE))
                        {
                            return instructionResult;
                        }
                    }
                } while ((Boolean)testInstruction.Code());
                return null;
            }

            public void AddInstruction(Instruction instruction)
            {
                if (this.repeatInstruction != null)
                {
                    if (this.repeatInstruction is InstructionBlock)
                    {
                        ((InstructionBlock)this.repeatInstruction).AddInstruction(instruction);
                    }
                    else
                    {
                        InstructionBlock instructionBlock = new InstructionBlock(Owner);
                        instructionBlock.AddInstruction(this.repeatInstruction);
                        instructionBlock.AddInstruction(instruction);
                        this.repeatInstruction = instructionBlock;
                    }
                }
                else
                {
                    this.repeatInstruction = instruction;
                }
            }

            public Instruction RemoveInstruction()
            {
                if (this.repeatInstruction != null)
                {
                    if (this.repeatInstruction is InstructionBlock)
                    {
                        InstructionBlock instructionBlock = (InstructionBlock)repeatInstruction;
                        if (instructionBlock.Instructions.Count > 2)
                        {
                            return instructionBlock.RemoveInstruction();
                        }
                        else
                        {
                            Instruction lastInstruction = instructionBlock.RemoveInstruction();
                            this.repeatInstruction = instructionBlock.RemoveInstruction();
                            return lastInstruction;
                        }
                    }
                    else
                    {
                        Instruction lastInstruction = this.repeatInstruction;
                        this.repeatInstruction = null;
                        return lastInstruction;
                    }
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Class which represents a for loop. It contains an assign instruction which is executed at the beginning, a test 
        /// instruction which must return a Boolean value, a stepInstruction which is executed after each repeat instruction
        /// execution and a repeat instruction which is executed until the test instruction returns false.<br />
        /// The repeat instruction can be a single instruction or an instruction block. In case of an instruction block if the
        /// block returns LoopSpecialSymbols.CONTINUE or LoopSpecialSymbols.BREAK the specified action is taken. If it returns
        /// null, the while continues with the step and test instructions. If it returns anything else then it is considered 
        /// a return value.
        /// </summary>
        protected class ForInstruction : Instruction, CodeContainer
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction assignInstruction, testInstruction, stepInstruction, repeatInstruction;

            public ForInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public object Code()
            {
                object instructionResult = null;
                assignInstruction.Code();
                while ((Boolean)testInstruction.Code())
                {
                    if (repeatInstruction != null)
                    {
                        instructionResult = repeatInstruction.Code();
                    }
                    if (instructionResult != null)
                    {
                        if (instructionResult.Equals(SpecialSymbols.BREAK))
                        {
                            break;
                        }
                        else if (!instructionResult.Equals(SpecialSymbols.CONTINUE))
                        {
                            return instructionResult;
                        }
                    }
                    stepInstruction.Code();
                }
                return null;
            }

            public void AddInstruction(Instruction instruction)
            {
                if (this.repeatInstruction != null)
                {
                    if (this.repeatInstruction is InstructionBlock)
                    {
                        ((InstructionBlock)this.repeatInstruction).AddInstruction(instruction);
                    }
                    else
                    {
                        InstructionBlock instructionBlock = new InstructionBlock(Owner);
                        instructionBlock.AddInstruction(this.repeatInstruction);
                        instructionBlock.AddInstruction(instruction);
                        this.repeatInstruction = instructionBlock;
                    }
                }
                else
                {
                    this.repeatInstruction = instruction;
                }
            }

            public Instruction RemoveInstruction()
            {
                if (this.repeatInstruction != null)
                {
                    if (this.repeatInstruction is InstructionBlock)
                    {
                        InstructionBlock instructionBlock = (InstructionBlock)repeatInstruction;
                        if (instructionBlock.Instructions.Count > 2)
                        {
                            return instructionBlock.RemoveInstruction();
                        }
                        else
                        {
                            Instruction lastInstruction = instructionBlock.RemoveInstruction();
                            this.repeatInstruction = instructionBlock.RemoveInstruction();
                            return lastInstruction;
                        }
                    }
                    else
                    {
                        Instruction lastInstruction = this.repeatInstruction;
                        this.repeatInstruction = null;
                        return lastInstruction;
                    }
                }
                else
                {
                    return null;
                }
            }
        }

        /* ******************** CONDITIONAL INSTRUCTION CLASSES ******************** */

        /// <summary>
        /// Class which represents an if-else instruction. The test instruction must return a Boolean value and it is executed
        /// first. The succeed instruction is executed if the test function returns true and the fail instruction is executed
        /// if the test function returns false. If there is no fail instruction (null) then the return value in case of fail is
        /// null, which means continuing the execution in the containing block. <br />
        /// The succeed and fail instructions can be single instructions or instruction blocks.
        /// </summary>
        protected class IfElseInstruction : Instruction, CodeContainer
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction testInstruction, succeedInstruction, failInstruction;

            public IfElseInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public object Code()
            {
                if ((Boolean)testInstruction.Code())
                {
                    if (succeedInstruction != null)
                    {
                        return succeedInstruction.Code();
                    }
                }
                else
                {
                    if (failInstruction != null)
                    {
                        return failInstruction.Code();
                    }
                }
                return null;
            }

            public void AddInstruction(Instruction instruction)
            {
                if (this.succeedInstruction != null)
                {
                    if (this.succeedInstruction is InstructionBlock)
                    {
                        ((InstructionBlock)this.succeedInstruction).AddInstruction(instruction);
                    }
                    else
                    {
                        InstructionBlock instructionBlock = new InstructionBlock(Owner);
                        instructionBlock.AddInstruction(this.succeedInstruction);
                        instructionBlock.AddInstruction(instruction);
                        this.succeedInstruction = instructionBlock;
                    }
                }
                else
                {
                    this.succeedInstruction = instruction;
                }
            }

            public Instruction RemoveInstruction()
            {
                if (this.succeedInstruction != null)
                {
                    if (this.succeedInstruction is InstructionBlock)
                    {
                        InstructionBlock instructionBlock = (InstructionBlock)succeedInstruction;
                        if (instructionBlock.Instructions.Count > 2)
                        {
                            return instructionBlock.RemoveInstruction();
                        }
                        else
                        {
                            Instruction lastInstruction = instructionBlock.RemoveInstruction();
                            this.succeedInstruction = instructionBlock.RemoveInstruction();
                            return lastInstruction;
                        }
                    }
                    else
                    {
                        Instruction lastInstruction = this.succeedInstruction;
                        this.succeedInstruction = null;
                        return lastInstruction;
                    }
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Class which represents a switch instruction. The argument instruction must return a value to compare with the
        /// ones in the case instructions. Once the argument matches a case value the case instructions begin to execute until
        /// one of them returns a BREAK or a value. The default instruction is executed at the end, if no other case returned
        /// BREAK or value.
        /// </summary>
        protected class SwitchInstruction : Instruction
        {
            public BaseXMLAutomaton Owner { get; set; }
            public List<CaseInstruction> caseInstructionsList;
            public Instruction argumentInstruction, defaultInstruction;

            public SwitchInstruction(BaseXMLAutomaton owner)
            {
                this.caseInstructionsList = new List<CaseInstruction>();
                this.Owner = owner;
            }

            public object Code()
            {
                int i = 0, size = caseInstructionsList.Count;
                object argument = argumentInstruction.Code(), aux = null;
                while (i < size && (!caseInstructionsList[i].label.Code().Equals(argument)))
                {
                    ++i;
                }
                while (i < size)
                {
                    if (caseInstructionsList.ElementAt(i) != null)
                    {
                        aux = caseInstructionsList[i].Code();
                    }
                    if (aux != null)
                    {
                        return aux.Equals(SpecialSymbols.BREAK) ? null : aux;
                    }
                    ++i;
                }
                if (defaultInstruction != null)
                {
                    aux = defaultInstruction.Code();
                }
                if (aux != null)
                {
                    return aux.Equals(SpecialSymbols.BREAK) ? null : aux;
                }
                else
                {
                    return null;
                }
            }

        }

        /// <summary>
        /// Class which represents a case instruction. This instruction has a label to be identified in the case.
        /// It acts like a normal Instruction which can contain inside code.
        /// </summary>
        protected class CaseInstruction : Instruction, CodeContainer
        {
            public BaseXMLAutomaton Owner { get; set; }
            public Instruction label;
            public Instruction instruction;

            public CaseInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public object Code()
            {
                if (instruction != null)
                {
                    return instruction.Code();
                }
                else
                {
                    return null;
                }
            }

            public void AddInstruction(Instruction instruction)
            {
                if (this.instruction != null)
                {
                    if (this.instruction is InstructionBlock)
                    {
                        ((InstructionBlock)this.instruction).AddInstruction(instruction);
                    }
                    else
                    {
                        InstructionBlock instructionBlock = new InstructionBlock(Owner);
                        instructionBlock.AddInstruction(this.instruction);
                        instructionBlock.AddInstruction(instruction);
                        this.instruction = instructionBlock;
                    }
                }
                else
                {
                    this.instruction = instruction;
                }
            }

            public Instruction RemoveInstruction()
            {
                if (this.instruction != null)
                {
                    if (this.instruction is InstructionBlock)
                    {
                        InstructionBlock instructionBlock = (InstructionBlock)instruction;
                        if (instructionBlock.Instructions.Count > 2)
                        {
                            return instructionBlock.RemoveInstruction();
                        }
                        else
                        {
                            Instruction lastInstruction = instructionBlock.RemoveInstruction();
                            this.instruction = instructionBlock.RemoveInstruction();
                            return lastInstruction;
                        }
                    }
                    else
                    {
                        Instruction lastInstruction = this.instruction;
                        this.instruction = null;
                        return lastInstruction;
                    }
                }
                else
                {
                    return null;
                }
            }
        }

        /* ******************** VALUE INSTRUCTION CLASSES ******************** */

        /// <summary>
        /// Class which represents a value. If it is a direct instruction in an instruction block then it's considered
        /// a return value for the block. If it's part of an expression inside an instruction then the instruction is 
        /// considered a normal value.
        /// </summary>
        protected class ValueInstruction : Instruction
        {
            public BaseXMLAutomaton Owner { get; set; }
            public object value;

            public ValueInstruction(BaseXMLAutomaton owner)
            {
                this.Owner = owner;
            }

            public object Code()
            {
                return value;
            }

        }
    }
}
